import MysqlHandler from './../utils/mysqlHandler';
import { Service, ServiceList } from './../utils/mysqlHandler';
import { AgentClient, State } from './AgentClient'
import ClientsManager from './ClientsManager';
import logger = require('../logger');
import Message from './Message'
namespace Agent {

    export class AgentManager {
        mysqlHandler: MysqlHandler;
        agentMap: Map<number, AgentClient>;
        timerId: NodeJS.Timeout | null = null;
        toClientMsgCallback: Message.onMessageCb | null = null;
        constructor() {
            this.mysqlHandler = new MysqlHandler('');
            this.agentMap = new Map<number, AgentClient>();
        }
        start() {
            if (this.timerId) {
                logger.error('AgentManager was already startup.');
                return;
            }
            this.timerId = setInterval(() => {
                this.loadServiceInfo();
            }, 1000 * 5);
            this.loadServiceInfo();
        }
        setToClientMsgCallback(cb: Message.onMessageCb) {
            this.toClientMsgCallback = cb;
        }

        /**
         * 函数功能：               处理从ClientsManager传递过来的客户端消息， 并转发给agent
         * @param data 
         */
        fromClientsMessage(data: Message.MsgBase): boolean {
            logger.info('client msg: ', data);
            switch (data.method) {
                case Message.Method.LOGIN_REQ: {
                    let loginReq: Message.LoginReq = <Message.LoginReq>(data);
                    let agent: AgentClient | undefined = this.agentMap.get(loginReq.serviceId);
                    if (!agent) {
                        logger.error('serviceid: ', loginReq.serviceId, ' is not exisits. data: ', loginReq);
                        return false;
                    }
                    agent.send(JSON.stringify(loginReq));
                    break;
                }
                case Message.Method.AICAHT_REQ: {
                    let aichatReq: Message.AiChatReq = <Message.AiChatReq>(data);
                    let agent: AgentClient | undefined = this.agentMap.get(aichatReq.serviceId);
                    if (!agent) {
                        logger.error('serviceid: ', aichatReq.serviceId, ' is not exisits. data: ', aichatReq);
                        return false;
                    }
                    agent.send(JSON.stringify(aichatReq));
                    break;
                }
                case Message.Method.LOGOUT_REQ: {
                    let logoutReq: Message.LoginReq = <Message.LoginReq>(data);
                    let agent: AgentClient | undefined = this.agentMap.get(logoutReq.serviceId);
                    if (!agent) {
                        logger.error('serviceid: ', logoutReq.serviceId, ' is not exisits. data: ', logoutReq);
                        return false;
                    }
                    agent.send(JSON.stringify(logoutReq));
                    break;
                }
            }
            return true;
        }


        /**
         * 函数功能:            根据现有的服务列表和数据库查询到的服务列表进行对比,判断是否有服务信息被删除,如果有则断开agent连接.并山出发服务信息.
         * @param sl 数据库中查询到的当前服务列表
         */
        private getDeleted(sl: ServiceList): Service[] {
            let deleted: Service[] = [];
            this.agentMap.forEach((v, k) => {
                let isExists: boolean = false;
                sl.servicdLists.forEach(item => {
                    if (k == item.serviceId) {
                        isExists = true;
                        return true;
                    }
                });
                if (!isExists) {//数据被删掉了.
                    deleted.push(v.getServiceInfo());
                }
            });
            return deleted;
        }
        /**
         * 函数功能:            根据现有的服务列表和数据库查询到的服务列表进行对比,
         *                      判断是否有服务信息变更,如果有变更则需要通知agent, 如果是agent地址变了,则需要断开连接重新连接到新的agent去
         * @param sl 数据库中查询到的当前服务列表
         */
        private getChanged(sl: ServiceList): Service[] {
            let changed: Service[] = [];
            sl.servicdLists.forEach(element => {
                element.serviceId;
                let agent: AgentClient | undefined = this.agentMap.get(element.serviceId);

                if (agent) {
                    // 已存在的
                    // 判断是否有信息改变.
                    if (!agent.getServiceInfo().equals(element)) {
                        changed.push(element);
                    }
                }
            });
            return changed;
        }
        /**
         * 函数功能:            根据现有的服务列表和数据库查询到的服务列表进行对比,判断是否有新增新的服务,如果新增则需要建立连接
         * @param sl 数据库中查询到的当前服务列表
         */
        private getNew(sl: ServiceList): Service[] {
            let newServices: Service[] = [];
            sl.servicdLists.forEach(element => {
                let agent: AgentClient | undefined = this.agentMap.get(element.serviceId);
                if (!agent) {
                    newServices.push(element);
                }
            });
            return newServices;
        }

        /**
         * 函数功能:            定时从数据库加载服务列表,检查是否有修改,增加,删除.
         */
        private loadServiceInfo() {
            this.mysqlHandler.getServices((err: Error | null, sl) => {
                if (err) {
                    logger.error('loadServiceInfo failed. errmsg: ', err.message, ' stack: ', err.stack);
                    return;
                }
                let deletedService: Service[] = this.getDeleted(sl);// 被删掉的
                let changedService: Service[] = this.getChanged(sl);// 被修改的
                let newService: Service[] = this.getNew(sl);// 新增的

                if (deletedService.length) {
                    this.procDeleted(deletedService);
                }
                if (newService.length) {
                    this.proceNew(newService)
                }
                if (changedService.length) {
                    this.procChanged(changedService);
                }
                logger.info('load ServiceInfo---------------------------------------------------------------------------------');
            });
        }
        /**
         * 函数功能:            处理新增的agent信息,基本上就是创建websocket连接.
         * @param newServices 
         */
        private proceNew(newServices: Service[]) {
            logger.info('new: ', newServices);
            newServices.forEach(item => {
                // this.serviceMap.delete(item.serviceId);
                this.agentMap.set(item.serviceId, new AgentClient(item,
                    (code: number, errmsg: string) => {
                        this.mysqlHandler.updateServiceState(item.serviceId, code, errmsg);
                    },
                    (data: string) => {
                        logger.info('on agent msg to client, data: ', data);
                        if (!this.toClientMsgCallback) {
                            logger.error('onclient msg callback is null, data: ', data);
                            return;
                        }
                        let msgBase: Message.MsgBase = JSON.parse(data);
                        this.toClientMsgCallback(msgBase);
                    })
                );
            });
        }

        /**
         * 函数功能:                处理被删除的agent(断开连接,并删除信息)
         * @param deletedServices 
         */
        private procDeleted(deletedServices: Service[]) {
            logger.info('deleted: ', deletedServices);
            // 处理已经被删掉的.
            deletedServices.forEach(item => {
                let agent: AgentClient | undefined = this.agentMap.get(item.serviceId);
                if (agent) {
                    agent.close();
                    this.agentMap.delete(item.serviceId);
                }
            });
            // 断开连接.
        }

        /**
         * 函数功能:                处理更新的agent(把更新后的内容发送给agent即可)
         * @param changedServices 
         */
        private procChanged(changedServices: Service[]) {
            logger.info('changed: ', changedServices);
            changedServices.forEach(item => {
                // this.serviceMap.delete(item.serviceId);
                let agent: AgentClient | undefined = this.agentMap.get(item.serviceId);
                if (agent) {
                    agent.updateServiceInfo(item);
                }
            });
        }
    }
}

export = Agent;